"""Contains the Ruin character, which is base for both player and monster"""

from pyre.core import actor, dice

class Statistic(object):
    """Base class for numbers used in the mechanics of Ruin"""

    def __init__(self, value):
        self.full = value
        self.curr = value

    def __iadd__(self, amount):
        self.curr += amount
        return self

    def __isub__(self, amount):
        self.curr -= amount
        return self

    def __int__(self):
        return int(self.curr)

    def __str__(self):
        return str(int(self))

    def __sub__(self, n):
        return int(self) - n

    def __add__(self, n):
        return int(self) + n

    def __mul__(self, n):
        return int(self) * n

    def __div__(self, n):
        return int(self) / n

    def __truediv__(self, n):
        return float(int(self)) / n

    def __floordiv__(self, n):
        return int(self) / n


class Vital(Statistic):
    """Represents a fluxuating Character stat which regenerates"""

    def __init__(self, value, gen_func):
        Statistic.__init__(self, value)
        self.gen_func = gen_func

    def generate(self):
        """
        Vital.generate(): None
        Performs the per-turn modification to the Vital
        """

        self.curr += self.gen_func(self.full, self.curr)


class Skill(Statistic):
    """Presents a bonus used for skill and combat rolls"""

    class RollResult(int):

        def __nonzero__(self):
            return self > 0

    def __init__(self, value, base_roll=20):
        Statistic.__init__(self, value)
        self.base_roll = dice.create_opposed(base_roll)

    def train(self):
        pass

    def skill_roll(self, opposing_skill, rating_modifer=None):
        rating = int(opposing_skill)
        if rating_modifer is not None:
            rating = rating_modifer(rating)

        result = Skill.RollResult(self.base_roll() + int(self) - rating)
        if result:
            self.train()
        else:
            opposing_skill.train()
        return result


def health_gen(full, curr):
    """
    health_gen(int, int): float
    Returns the amount that health should regenerate
    """

    return .01 * full if curr < full else 0


class Character(actor.Actor):
    """Abstract base class for both Player and Monster"""

    def __init__(self, face, name, hp):
        actor.Actor.__init__(self, face, name)
        self.hp = Vital(hp, health_gen)

    def stat_regen(self):
        self.hp.generate()

    def set_pos(self, pos):
        bumped = self.world.get_actor_at(pos, Character)
        if bumped and bumped is not self:
            self.perform_melee(bumped)
        elif self.world.passable_at(pos):
            actor.Actor.set_pos(self, pos)

    def perform_melee(self, bumped):
        """
        Character.perform_melee(Character): None
        This Character strikes the other, possibly dealing damage
        """

        if self.melee.skill_roll(bumped.evade):
            damage = dice.roll_xdy(self.weapon) - dice.roll_xdy(bumped.armor)
            if damage > 0:
                bumped.hp -= damage
                self.append_message('%s <hit> %o', self, bumped)
                if int(bumped.hp) <= 0:
                    bumped.expire()
                    bumped.append_message('%s <die>', bumped)
            else:
                self.append_message('%s <graze> %o', self, bumped)
        else:
            self.append_message('%s <miss> %o', self, bumped)

    def perform_ranged(self, target):
        penalty = self.pos.steps_to(target.pos) / 5
        if self.melee.skill_roll(target.evade, lambda x: x / 2 + penalty):
            damage = dice.roll_xdy(self.ranged_damage)
            damage -= dice.roll_xdy(target.armor)
            if damage > 0:
                target.hp -= damage
                self.append_message('%s <shoot> %o', self, target)
                if int(target.hp) <= 0:
                    target.expire()
                    target.append_message('%s <die>', target)
            else:
                self.append_message('%s <graze> %o', self, target)
        else:
            self.append_message('%s <miss> %o', self, target)
